﻿using Furion.CMS.Application.Dto;
using Furion.CMS.Core;
using Furion.CMS.Core.EventSource;
using Furion.CMS.Domain;
using Furion.CMS.EntityFramework.Core;
using Furion.DatabaseAccessor;
using Furion.DistributedIDGenerator;
using Furion.DynamicApiController;
using Furion.EventBus;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Furion.CMS.Application
{
    /// <summary>
    /// 组织机构
    /// </summary>
    [DisplayName("OrganizationManager")]
    public class OrganizationAppService : BaseApplication
    {
        private readonly IRepository<OrganizationInfo> _appRepository;
        private readonly IRepository<UserInfo> _userRepository;
        private readonly IRepository<RoleInfo> _roleRepository;
        private readonly IDistributedIDGenerator _idGenerator;

        public OrganizationAppService(IRepository<OrganizationInfo> appRepository,
            IRepository<UserInfo> userRepository,
            IRepository<RoleInfo> roleRepository,
            IDistributedIDGenerator idGenerator)
        {
            _appRepository = appRepository;
            _userRepository = userRepository;
            _roleRepository = roleRepository;
            _idGenerator = idGenerator;
        }

        /// <summary>
        /// 获取组织机构树
        /// </summary>
        /// <param name="parentId"></param>
        /// <returns></returns>
        [QueryParameters]
        public async Task<List<OrgDto>> GetTreeAsync([RouteConstraint(":guid")] Guid? parentId = default)
        {
            //TODO:通过节点路径快速的定位指定节点下的所有数据
            var result = await _appRepository.Where(parentId.HasValue, t => EF.Functions.Like(t.Path, $"%{parentId}%"))
                .Include(i => i.Children)
                .OrderBy(o => o.OrderId)
                .ToListAsync();

            return result.Where(t => t.ParentId == parentId).Adapt<List<OrgDto>>();
        }

        /// <summary>
        /// 获取组织机构详情
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [SecurityDefine]
        public OrgDetailDto GetDetail([RouteConstraint(":guid")] Guid id)
        {
            var detail = _appRepository.AsQueryable(false)
                .FirstOrDefault(x => x.Id == id)
                .Adapt<OrgDetailDto>();

            return detail;
        }

        /// <summary>
        /// 创建组织机构
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [UnitOfWork]
        [SecurityDefine]
        public async Task<List<OrgDto>> Create([FromBody] OrgCreateAndUpdateDto dto)
        {
            var entity = dto.Adapt<OrganizationInfo>();
            var query = _appRepository.AsQueryable(false);
            // 创建时序Guid
            entity.Id = _idGenerator.Create().ToTryGuid();
            entity.Path = $"{entity.Id}";
            // 检测是否存在父级
            if (dto.ParentId.HasValue)
            {
                var parent = await query.FirstOrDefaultAsync(t => t.Id == dto.ParentId);
                if (parent == null)
                    throw Oops.Bah($"未能找到ID为 {dto.ParentId} 的父节点");
                entity.Path = $"{parent.Path}/{entity.Id}";
            }
            // 自动排序
            var historyDatas = query.Where(t => t.ParentId == entity.ParentId).ToList();
            if (historyDatas.Any()) entity.OrderId = historyDatas.Max(m => m.OrderId) + 1;

            await _appRepository.InsertNowAsync(entity);

            return await GetTreeAsync();
        }

        /// <summary>
        /// 更新组织机构信息
        /// </summary>
        /// <param name="id"></param>
        /// <param name="dto"></param>
        [UnitOfWork]
        [SecurityDefine]
        public async Task<List<OrgDto>> Update([RouteConstraint(":guid")] Guid id, [FromBody] OrgCreateAndUpdateDto dto)
        {
            var input = dto.Adapt<OrganizationInfo>();
            var query = _appRepository.AsQueryable(false);

            var history = await query.FirstOrDefaultAsync(x => x.Id == id);
            if (history == null)
                throw Oops.Bah($"未能找到ID为 {id} 的数据");

            // 父节点检测
            var parent = await query.FirstOrDefaultAsync(x => x.Id == input.ParentId);
            if (input.ParentId.HasValue && parent == null)
                throw Oops.Bah($"未能找到ID为 {dto.ParentId} 的父节点");

            if (input.ParentId != history.ParentId)
            {// 当父级发生变化时
                input.OrderId = 0; // 重置为0
                input.Path = input.ParentId.HasValue ? $"{parent.Path}/{id}" : $"{id}";
            }
            else
            {
                input.OrderId = history.OrderId;
            }

            var update = history.AsMergeObject(input, "Id", "CreatedTime", "IsDelete");
            update.UpdatedTime = DateTimeOffset.UtcNow;
            await _appRepository.UpdateNowAsync(update, true);

            return await GetTreeAsync();
        }

        /// <summary>
        /// 更新组织机构的层级与排序并返回最新数据
        /// </summary>
        /// <param name="id">操作id</param>
        /// <param name="changeId">变更id</param>
        /// <param name="dropToGap">传入Antd Tree 拖动事件的 dropToGap 值</param>
        /// <returns></returns>
        [UnitOfWork]
        [HttpPatch]
        [SecurityDefine($"OrganizationManager_{nameof(Update)}")]
        public async Task<List<OrgDto>> UpdateTreeAsync([RouteConstraint(":guid"), ApiSeat(ApiSeats.ActionStart)] Guid id,
            [RouteConstraint(":guid")] Guid changeId,
            [FromQuery] bool dropToGap)
        {
            var query = _appRepository.AsQueryable(false);
            var currentOrg = query.Where(x => x.Id == id).FirstOrDefault();
            var valueOrg = query.Where(x => x.Id == changeId).FirstOrDefault();

            if (currentOrg == null)
                throw Oops.Bah($"未能找到ID为 {id} 的数据");

            if (valueOrg == null)
                throw Oops.Bah($"未能找到ID为 {changeId} 的数据");

            // 变更当前数据后先提交更新
            if (dropToGap)
            {// 当前拖动的索引尚未到达0
                currentOrg.ParentId = valueOrg.ParentId;
                currentOrg.OrderId = valueOrg.OrderId + 1; // 可能超限
            }
            else
            {// 当前拖动的索引已到达0
                currentOrg.ParentId = valueOrg.Id;
                currentOrg.Path = $"{valueOrg.Path}/{currentOrg.Id}";
                currentOrg.OrderId = 0;
            }
            currentOrg.UpdatedTime = DateTimeOffset.UtcNow;
            await _appRepository.UpdateNowAsync(currentOrg);

            // 获取当前操作的同级机构列表
            var endItems = await _appRepository.Where(x => x.Id == currentOrg.ParentId)
                 .Include(i => i.Children)
                 .SelectMany(s => s.Children)
                 .OrderBy(o => o.OrderId)
                 .ThenByDescending(o => o.UpdatedTime)
                 .ToListAsync();

            // 同步更改排序Id
            for (int i = 0; i < endItems.Count; i++)
            {
                var item = endItems[i];
                item.OrderId = i;
            }

            return await GetTreeAsync();
        }

        /// <summary>
        /// 删除组织机构,该操作会删除包含的下级数据
        /// </summary>
        /// <param name="eventPublisher"></param>
        /// <param name="id"></param>
        [SecurityDefine]
        public async Task<List<OrgDto>> Delete([FromServices] IEventPublisher eventPublisher, [RouteConstraint(":guid")] Guid id)
        {
            // 先查出全部机构
            var orgList = await _appRepository.Include(i => i.Users)
                .Include(i => i.Children)
                .ToListAsync();

            var entity = orgList.FirstOrDefault(t => t.Id == id);

            if (entity == null)
                throw Oops.Bah($"未能找到ID为 {id} 的数据");

            var users = new List<UserInfo>().AsEnumerable();

            // 递归循环标记删除
            void loop(IEnumerable<OrganizationInfo> list)
            {
                foreach (var child in list)
                {
                    users = users.UnionBy(child.Users, u => u.Id);
                    loop(child.Children);
                    _appRepository.Delete(child);
                }
            }

            users = users.UnionBy(entity.Users, u => u.Id);
            loop(entity.Children);
            _appRepository.Delete(entity);
            await _appRepository.SaveNowAsync();

            // 通知前端更换token
            var triggerEvetns = users.Select(s => new OnTriggerOuterEvent(OuterEventCategory.UserRefresh, s.Id.ToString()));
            eventPublisher?.PublishDelayAsync(new ChannelEventSource(EventIdConsts.TriggerManyOuterEvent, triggerEvetns), 1000);

            return await GetTreeAsync();
        }

        /// <summary>
        /// 获取指定机构的成员
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<List<UserDto>> GetUsersAsync([RouteConstraint(":guid"), ApiSeat(ApiSeats.ActionStart)] Guid id)
        {
            var result = await _appRepository.AsQueryable(false)
                    .Where(x => x.Id == id)
                    .Include(i => i.Users)
                    .SelectMany(s => s.Users)
                    .ProjectToType<UserDto>()
                    .ToListAsync();

            return result.DistinctBy(d => d.Id).ToList(); // 根据id进行去重
        }

        /// <summary>
        /// 更新机构关联用户
        /// </summary>
        /// <param name="eventPublisher"></param>
        /// <param name="id"></param>
        /// <param name="relatedList"></param>
        /// <returns></returns>
        [UnitOfWork]
        [SecurityDefine]
        public async Task UpdateUsers([FromServices] IEventPublisher eventPublisher,
            [RouteConstraint(":guid"), ApiSeat(ApiSeats.ActionStart)] Guid id, [FromBody] List<int> relatedList)
        {
            var entity = await _appRepository.Where(x => x.Id == id).Include(i => i.Users).FirstOrDefaultAsync();
            var relatedUsers = await _userRepository.Where(t => relatedList.Contains(t.Id)).ToListAsync();
            entity.Users.ToCollectionChange(relatedUsers, f => f.Id);

            // 通知前端更换token
            var triggerEvetns = relatedList.Select(s => new OnTriggerOuterEvent(OuterEventCategory.UserRefresh, s.ToString()));
            eventPublisher?.PublishDelayAsync(new ChannelEventSource(EventIdConsts.TriggerManyOuterEvent, triggerEvetns), 1000);
        }

        /// <summary>
        /// 删除机构关联用户
        /// </summary>
        /// <param name="eventPublisher"></param>
        /// <param name="id"></param>
        /// <param name="relatedList"></param>
        /// <returns></returns>
        [UnitOfWork]
        [SecurityDefine]
        public async Task DeleteUsers([FromServices] IEventPublisher eventPublisher,
            [RouteConstraint(":guid"), ApiSeat(ApiSeats.ActionStart)] Guid id, [FromBody] List<int> relatedList)
        {
            var entity = await _appRepository.Where(x => x.Id == id).Include(i => i.Users).FirstOrDefaultAsync();
            var relatedUsers = await _userRepository.AsQueryable(true).Where(t => relatedList.Contains(t.Id)).ToListAsync();
            foreach (var user in relatedUsers)
            {
                entity.Users.Remove(user);
            }

            // 通知前端更换token
            var triggerEvetns = relatedList.Select(s => new OnTriggerOuterEvent(OuterEventCategory.UserRefresh, s.ToString()));
            eventPublisher?.PublishDelayAsync(new ChannelEventSource(EventIdConsts.TriggerManyOuterEvent, triggerEvetns), 1000);
        }

        /// <summary>
        /// 获取指定机构的角色
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<List<RoleDto>> GetRolesAsync([RouteConstraint(":guid"), ApiSeat(ApiSeats.ActionStart)] Guid id)
        {
            var result = await _appRepository.AsQueryable(false)
                   .Where(x => x.Id == id)
                   .Include(i => i.Roles)
                   .SelectMany(s => s.Roles)
                   .ProjectToType<RoleDto>()
                   .ToListAsync();

            return result.DistinctBy(d => d.Id).ToList(); // 根据id进行去重
        }

        /// <summary>
        /// 更新机构关联角色
        /// </summary>
        /// <param name="eventPublisher"></param>
        /// <param name="id"></param>
        /// <param name="relatedList"></param>
        /// <returns></returns>
        [UnitOfWork]
        [SecurityDefine]
        public async Task UpdateRoles([FromServices] IEventPublisher eventPublisher,
            [RouteConstraint(":guid"), ApiSeat(ApiSeats.ActionStart)] Guid id, [FromBody] List<int> relatedList)
        {
            var entity = await _appRepository.Where(x => x.Id == id)
                .Include(i => i.Roles)
                .Include(i => i.Users)
                .AsSplitQuery()
                .FirstOrDefaultAsync();
            var relatedRoles = await _roleRepository.Where(t => relatedList.Contains(t.Id)).ToListAsync();
            entity.Roles.ToCollectionChange(relatedRoles, f => f.Id);

            // 通知前端更换token
            var triggerEvetns = entity.Users.Select(s => new OnTriggerOuterEvent(OuterEventCategory.UserRefresh, s.Id.ToString()));
            eventPublisher?.PublishDelayAsync(new ChannelEventSource(EventIdConsts.TriggerManyOuterEvent, triggerEvetns), 1000);
        }

        /// <summary>
        /// 删除机构关联角色
        /// </summary>
        /// <param name="eventPublisher"></param>
        /// <param name="id"></param>
        /// <param name="relatedList"></param>
        /// <returns></returns>
        [UnitOfWork]
        [SecurityDefine]
        public async Task DeleteRoles([FromServices] IEventPublisher eventPublisher,
            [RouteConstraint(":guid"), ApiSeat(ApiSeats.ActionStart)] Guid id, [FromBody] List<int> relatedList)
        {
            var entity = await _appRepository.Where(x => x.Id == id)
                .Include(i => i.Roles)
                .Include(i => i.Users)
                .AsSplitQuery()
                .FirstOrDefaultAsync();
            var relatedRoles = await _roleRepository.Where(t => relatedList.Contains(t.Id)).ToListAsync();

            foreach (var user in relatedRoles)
            {
                entity.Roles.Remove(user);
            }

            // 通知前端更换token
            var triggerEvetns = entity.Users.Select(s => new OnTriggerOuterEvent(OuterEventCategory.UserRefresh, s.Id.ToString()));
            eventPublisher?.PublishDelayAsync(new ChannelEventSource(EventIdConsts.TriggerManyOuterEvent, triggerEvetns), 1000);
        }
    }
}
